#region Copyright Notice

// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 

#endregion

using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace AbstractClass.LiveUpdate
{
    /// <summary>
    /// Represents a type to xml (de)serialize an object which implements <see cref="IXObject"/>.
    /// </summary>
    /// <typeparam name="T">The type to (de)serialize which implements <see cref="IXObject"/>.</typeparam>
    public class XObjectSerializer<T> where T : IXObject, new()
    {
        private readonly Type _type;
        private XmlSerializer _xmlSerializer;

        /// <summary>
        ///   Initializes a new instance of the <see cref = "XObjectSerializer&lt;T&gt;" /> class.
        /// </summary>
        public XObjectSerializer()
        {
            _type = typeof (T);
            _xmlSerializer = new XmlSerializer(_type);
        }

        /// <summary>
        ///  Deserializes a xml string to an object of type <typeparamref name="T"/>.
        /// </summary>
        /// <param name="xml">Xml string.</param>
        /// <returns>An object of type <typeparamref name="T"/> obtained after deserialization.</returns>
        public T Deserialize(string xml)
        {
            using (TextReader reader = new StringReader(xml))
            {
                return Deserialize(reader);
            }
        }

        /// <summary>
        /// Deserializes a <see cref="XmlDocument"/> to an object of type <typeparamref name="T"/>.
        /// </summary>
        /// <param name="document">The <see cref="XmlDocument"/> to deserialize.</param>
        /// <returns>An object of type <typeparamref name="T"/> obtained after deserialization.</returns>
        public T Deserialize(XmlDocument document)
        {
            using (TextReader reader = new StringReader(document.OuterXml))
            {
                return Deserialize(reader);
            }
        }

        /// <summary>
        /// Deserializes a <see cref="TextReader"/> containing xml data to an object of type <typeparamref name="T"/>.
        /// </summary>
        /// <param name="reader">A <see cref="System.IO.TextReader"/> used to read xml string.</param>
        /// <returns>An object of type <typeparamref name="T"/> obtained after deserialization.</returns>
        public T Deserialize(TextReader reader)
        {
            var o = (T) _xmlSerializer.Deserialize(reader);
            o.Init();
            reader.Close();
            return o;
        }

        /// <summary>
        /// Serializes an object of type <typeparamref name="T"/> to an <see cref = "System.Xml.XmlDocument" /> object.
        /// </summary>
        /// <param name="xObj">An object to serialize.</param>
        /// <returns>A <see cref = "System.Xml.XmlDocument" /> instance containing serialization data.</returns>
        public XmlDocument Serialize(T xObj)
        {
            string xml = StringSerialize(xObj);
            var doc = new XmlDocument {PreserveWhitespace = true};
            doc.LoadXml(xml);
            doc = Clean(doc);
            return doc;
        }

        /// <summary>
        /// Serializes an object of type <typeparamref name="T"/> to a xml string.
        /// </summary>
        /// <param name="xObj">An object to serialize.</param>
        /// <returns>XML string containing serialization data of <paramref name="xObj"/>.</returns>
        public string StringSerialize(T xObj)
        {
            TextWriter w = WriterSerialize(xObj);
            string xml = w.ToString();
            w.Close();
            return xml.Trim();
        }

        /// <summary>
        /// Returns a <see cref="TextWriter"/> containing the serialization data of an object
        /// of type <typeparamref name="T"/>.
        /// </summary>
        /// <param name="xObj">An object to serialize.</param>
        /// <returns>A <see cref="System.IO.TextWriter"/> object containing serialization data of an object.</returns>
        public TextWriter WriterSerialize(T xObj)
        {
            using (TextWriter w = new StringWriter(CultureInfo.CurrentCulture))
            {
                xObj.Close();
                _xmlSerializer = !String.IsNullOrEmpty(xObj.DefaultNamespace)
                                     ? new XmlSerializer(_type, xObj.DefaultNamespace)
                                     : new XmlSerializer(_type);

                _xmlSerializer.Serialize(w, xObj);
                w.Flush();
                return w;
            }
        }

        private static XmlDocument Clean(XmlDocument doc)
        {
            doc.RemoveChild(doc.FirstChild);
            XmlNode first = doc.FirstChild;
            foreach (XmlNode n in doc.ChildNodes)
            {
                if (n.NodeType != XmlNodeType.Element) continue;
                first = n;
                break;
            }
            if (first.Attributes != null)
            {
                XmlAttribute a = first.Attributes["xmlns:xsd"];
                if (a != null)
                {
                    first.Attributes.Remove(a);
                }
                a = first.Attributes["xmlns:xsi"];
                if (a != null)
                {
                    first.Attributes.Remove(a);
                }
            }
            return doc;
        }

        /// <summary>
        /// Deserializes an object of type <typeparamref name="T"/> from a xml file.
        /// </summary>
        /// <param name="file">XML file path.</param>
        /// <returns>Deserialized object of type <typeparamref name="T"/>.</returns>
        public static T ReadFile(string file)
        {
            T xObj;
            var serializer = new XObjectSerializer<T>();
            try
            {
                string xml;
                using (var reader = new StreamReader(file))
                {
                    xml = reader.ReadToEnd();
                }

                xObj = serializer.Deserialize(xml);
                return xObj;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
            }
            xObj = new T();
            xObj.Init();
            return xObj;
        }


        /// <summary>
        /// Serializes an object of type <typeparamref name="T"/> to a xml file.
        /// </summary>
        /// <param name="file">XML file path.</param>
        /// <param name="xObj">An object of type <typeparamref name="T"/> to serialize.</param>
        /// <returns><c>true</c> if operation is successful; otherwise, <c>false</c>.</returns>
        public static bool WriteFile(string file, T xObj)
        {
            bool ok = false;
            var serializer = new XObjectSerializer<T>();
            try
            {
                string xml = serializer.Serialize(xObj).OuterXml;
                using (var writer = new StreamWriter(file, false))
                {
                    writer.Write(xml.Trim());
                    writer.Flush();
                }
                ok = true;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
            }
            return ok;
        }
    }
}